﻿/* 
 * Copyright 2010 Justin Pealing
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 *     
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. 
 */

using System;
using System.IO;
using Minnow.Web.CGI;
using Minnow.Web.FastCGI;
using Minnow.Web.Http;

namespace Minnow.Web
{
    /// <summary>
    /// Handles HTTP requests via a FastCGI application.
    /// </summary>
    public class FastCGIHandler : RequestHandler
    {
        private FastCGIProcess process;
        private object requestLock = new object();

        /// <summary>
        /// Initialises a new instance of <see cref="FastCGIHandler" />.
        /// </summary>
        public FastCGIHandler()
        {

        }

        /// <summary>
        /// Initialises a new instance of <see cref="FastCGIHandler" />.
        /// </summary>
        /// <param name="CommandLine">The command line to the FastCGI application</param>
        public FastCGIHandler(string CommandLine)
        {
            this.CommandLine = CommandLine;
        }

        /// <summary>
        /// Gets and sets the command line to the FastCGI application
        /// </summary>
        public string CommandLine
        {
            get;
            set;
        }

        /// <summary>
        /// Gets the underlying FastCGI process, or creates one if it doesn't exist.
        /// </summary>
        /// <returns>FastCGI process.</returns>
        private FastCGIProcess GetProcess()
        {
            // TODO: Concurrency and locking
            if (this.process == null)
            {
                this.process = FastCGIProcess.StartProcess(this.CommandLine);
            }
            return this.process;
        }

        #region IHttpHandler Members

        public override void Process(IHttpRequest Request, IHttpResponse Response, string FilePath)
        {
            // TODO: Better thread safety / throttling of requests.
            lock (this.requestLock)
            {
                // TODO: Much better error handling
                FastCGIProcess process = this.GetProcess();

                FastCGIRequest fastCGIRequest = process.StartRequest(Role.FCGI_RESPONDER, true);

                fastCGIRequest.SendParams(CGIUtils.GetEnvironmentVariables(Request, FilePath));
                fastCGIRequest.SendParams(null);

                StreamUtils.CopyStream(Request.InputStream, fastCGIRequest.StandardInput);
                fastCGIRequest.StandardInput.Close();

                ProtocolStatus status = fastCGIRequest.WaitForRequestEnd();
                if (status != ProtocolStatus.FCGI_REQUEST_COMPLETE)
                {
                    throw new Exception();
                }

                CGIUtils.ParseCgiResponse(
                    Response,
                    new StreamReader(fastCGIRequest.StandardOutput),
                    new StreamReader(fastCGIRequest.StandardError),
                    fastCGIRequest.AppStatus);
            }
        }

        #endregion
    }
}
